import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import Jama.Matrix;


public class Forcasting {
	
	
	
	private double[][] test_data;
	
	private double[][] original_data;
	
	public Forcasting() throws IOException{
		
		ReadExcel test = new ReadExcel();
		test.setInputFile("c:/CoolingLoadProject/test.xls");
		
		ReadExcel original = new ReadExcel();
		original.setInputFile("c:/CoolingLoadProject/Original data.xls");
		
		test_data = test.read();
		
		original_data = original.read();
		
		
	}
	
	/**
	 * Read data from original file
	 * @return
	 * @throws IOException
	 */
	public List<Matrix> readOriginal() throws IOException{
	    
	    
	    double[] array_Y = new double[original_data.length];
	    double[][] array_X = new double[original_data.length][17];
	    
	    
	    
	    for(int i = 0; i < original_data.length; i++){
	    	array_Y[i] = original_data[i][0];
	    	for(int j = 0; j < 11; j++){	
	    		array_X[i][j] = original_data[i][j+1];
	    	}
	    }
	    
	    for(int i = 0; i < original_data.length; i++){
	    		//t*t
	    		array_X[i][11] = original_data[i][6] * original_data[i][6];
	    		//t24*t24
	    		array_X[i][12] = original_data[i][7] * original_data[i][7];
	    		//t168*t168
	    		array_X[i][13] = original_data[i][8] * original_data[i][8];
	    		//t * RHt
	    		array_X[i][14] = original_data[i][6] * original_data[i][9];
	    		//t24 *RHt24
	    		array_X[i][15] = original_data[i][7] * original_data[i][10];
	    		//t168 * RHt168
	    		array_X[i][16] = original_data[i][8] * original_data[i][11];
	    	
	    }
	    
	    List<Matrix> result = new ArrayList<Matrix>();
	    Matrix X = new Matrix(array_X);
	    Matrix Y = new Matrix(array_Y, array_Y.length);
	    
	    result.add(X);
	    result.add(Y);
	    
	    return result;
	}
	
	
	
	/**
	 * Read the data from test file
	 * @return
	 * @throws IOException
	 */
	public List<Matrix> readTestdata() throws IOException{
	    
	    
	    double[] array_Y = new double[test_data.length];
	    double[][] array_X = new double[test_data.length][17];
	    
	    
	    
	    for(int i = 0; i < test_data.length; i++){
	    	array_Y[i] = test_data[i][0];
	    	for(int j = 0; j < 11; j++){	
	    		array_X[i][j] = test_data[i][j+1];
	    	}
	    }
	    
	    for(int i = 0; i < test_data.length; i++){
	    		//t*t
	    		array_X[i][11] = test_data[i][6] * test_data[i][6];
	    		//t24*t24
	    		array_X[i][12] = test_data[i][7] * test_data[i][7];
	    		//t168*t168
	    		array_X[i][13] = test_data[i][8] * test_data[i][8];
	    		//t * RHt
	    		array_X[i][14] = test_data[i][6] * test_data[i][9];
	    		//t24 *RHt24
	    		array_X[i][15] = test_data[i][7] * test_data[i][10];
	    		//t168 * RHt168
	    		array_X[i][16] = test_data[i][8] * test_data[i][11];
	    	
	    }
	    
	    List<Matrix> result = new ArrayList<Matrix>();
	    Matrix X = new Matrix(array_X);
	    Matrix Y = new Matrix(array_Y, array_Y.length);
	    
	    result.add(X);
	    result.add(Y);
	    
	    return result;
	} 
	
	
	 
	
	/**
	 * Add new line of data after the previous data set
	 * @param previousData
	 * @param testData
	 * @param num
	 * @return
	 * @throws IOException
	 */
	public List<Matrix> addNewData(List<Matrix> previousData, List<Matrix> testData, int num) throws IOException{
		
		Matrix x = previousData.get(0);
		Matrix test_x = testData.get(0);
		Matrix y = previousData.get(1);
		Matrix test_y = testData.get(1);
		
		int length = x.getArray().length+1;
		
		double[][] array_Pre_x = new double[length][17];
		
		double[] array_Pre_y = new double[length];
		
		
		
		array_Pre_x = Arrays.copyOfRange(x.getArray(), 0, x.getArray().length + 1);
		
		array_Pre_y = Arrays.copyOf(y.getRowPackedCopy(), length);
		
		// add new data to the last index of the previous matrix.
		array_Pre_x[length - 1] = test_x.getArray()[num];
		
		array_Pre_y[array_Pre_y.length-1] = test_y.getRowPackedCopy()[num];
		 
		
		Matrix current_x = new Matrix(array_Pre_x);
		
		Matrix current_y = new Matrix(array_Pre_y, array_Pre_y.length);
		
		List<Matrix> result = new ArrayList<Matrix>();
		
		result.add(current_x);
		result.add(current_y);
		
		return result;
			
	}
	
	/**
	 * get the prediction y 
	 * y=x*b;
	 * @param currentData a list of Matrix X and Y for current Data
	 * @param previousData a list of Matrix X and Y for previous Data
	 * @return
	 * @throws IOException
	 */
	public List<Double> getPreY(List<Matrix>currentData, List<Matrix> previousData) throws IOException{
		
		Matrix currentData_x = currentData.get(0);
		
		Matrix newData = new Matrix(currentData_x.getArray()[currentData_x.getArray().length - 1], 17);
		
		newData = newData.transpose();
		
		List<Matrix> b_lumbda_list = calculateForgetFactorCoefficient(previousData);
		
		List<Double> y_b_lumbda_list = new ArrayList<Double>();
		
		double lumbda = 0.971d;
		
		for(Matrix b: b_lumbda_list){
			
			double pre_y = newData.times(b).get(0, 0); 
			System.out.println(lumbda + " pre_y: " + pre_y);
			y_b_lumbda_list.add(pre_y);
			
			lumbda += 0.001;
		}
		
		 return y_b_lumbda_list;
		
	}
	
	
	
	
	
	/**
	 * Calculate a list of coefficient based on the forgetting factor
	 * @param data a list of Matrix X and Y
	 * @return
	 * @throws IOException
	 */
	public List<Matrix> calculateForgetFactorCoefficient(List<Matrix> data) throws IOException{
		
		Matrix x = data.get(0);
		Matrix y = data.get(1);
		
		double[][] array_X = x.getArray();
		
		double[] array_Y = y.getRowPackedCopy();
		
		int length = x.getArray().length;
		
		Matrix weight = Matrix.identity(length, length);
		
		Regression regression = new Regression();
		
		weight = regression.getIterWeight(array_Y, array_X, weight);
		
		List<double[][]> weightPrimePrime = new ArrayList<double[][]>();
		
		weightPrimePrime = regression.getWeightPrimePrime(weight);
		
		List<Matrix> b_lumbda_list = regression.getFFCofficient(array_Y, array_X, weightPrimePrime);
		
		return b_lumbda_list;
		
	}
	
	public double[] calculateWeight(List<Matrix> data) throws IOException{
		
		Matrix x = data.get(0);
		Matrix y = data.get(1);
		
		double[][] array_X = x.getArray();
		
		double[] array_Y = y.getRowPackedCopy();
		
		int length = x.getArray().length;
		
		Matrix weight = Matrix.identity(length, length);
		
		Regression regression = new Regression();
		
		weight = regression.getIterWeight(array_Y, array_X, weight);
		
		List<double[][]> weightPrimePrime = new ArrayList<double[][]>();
		
		weightPrimePrime = regression.getWeightPrimePrime(weight);
		
		double[] result = new double[weightPrimePrime.get(0).length];
		
		for(int i = 0; i < weightPrimePrime.get(0).length; i++){
			result[i] = weightPrimePrime.get(0)[i][i];
		}
		
		return result;
		
	}
	
	/**
	 * get the list of the predication Cooling load at t0 based on lumbda from 0.971 to 1.000
	 * 
	 * @param round
	 * @return
	 * @throws IOException
	 */
	public List<List<Double>> getPreYList(List<Matrix> original_data, List<Matrix> test_data, int round) throws IOException{
		
		
		List<Matrix> currentData = addNewData(original_data, test_data, 0);
		
		System.out.println("Round: 0");
		
		List<Double> FirstRoundpreY = getPreY(currentData, original_data);
		
		List<List<Double>> preY_List = new ArrayList<List<Double>>();
		
		
		preY_List.add(FirstRoundpreY);
		
		
		
		
		for(int i = 1; i < round; i++){
			
			System.out.println("Round: " + i);
			
			List<Matrix> previousData = currentData;
			
			currentData = addNewData(previousData, test_data, i);
			
			List<Double> preY = getPreY(currentData, previousData);
			
			preY_List.add(preY);
			
			
		}
		
		return preY_List;
		
	}
	
	
	public List<List<Double>> checkOutlier(List<Matrix> original_data, List<Matrix> test_data) throws IOException{
		
		List<Matrix> currentData = original_data;
		
		List<List<Double>> pre_y_list = getPreYList(original_data, test_data, 5);
		
		List<List<Double>> newY_List = new ArrayList<List<Double>>();
		
		
		for(int i = 0; i < 5; i++){
			
			System.out.println("Round: " + i);
			
			List<Matrix> previousData = currentData;
			
			currentData = addNewData(previousData, test_data, i);
			
			int length = currentData.get(0).getArray().length;
			
			double[] weight = calculateWeight(currentData);
			
			
			
			//check whether weight t-24 is less than 0.3 
			if(weight[1] < 0.3 && i == 4){
				// change the t24 to prediction t24
				currentData.get(0).getArray()[length - 1][1] = pre_y_list.get(i-1).get(0);
			}
			if(weight[2] < 0.3 && i == 4){
				// change the t168 to prediction t168
				currentData.get(0).getArray()[length - 1][2] = pre_y_list.get(i-4).get(0);
			}
			
			List<Double> preY = getPreY(currentData, previousData);
			
			newY_List.add(preY);
			
			
		}
		
		
		return newY_List;
		
		
		
	}

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		Forcasting forcasting = new Forcasting();
		
		List<Matrix> original_data = forcasting.readOriginal();
		
		List<Matrix> test_data = forcasting.readTestdata();
		
		forcasting.checkOutlier(original_data, test_data);
		
		

	}

}
